package com.douziit.meilianoa.base.config.filter;


import com.douziit.meilianoa.base.annotation.*;
import com.douziit.meilianoa.base.constant.HeaderNameConstant;
import com.douziit.meilianoa.base.constant.SecurityConstant;
import com.douziit.meilianoa.base.enums.ResponseCode;
import com.douziit.meilianoa.base.exception.TipException;
import com.douziit.meilianoa.base.redis.UserTokenService;
import com.douziit.meilianoa.base.utils.JacksonUtil;
import com.douziit.meilianoa.base.utils.Sign;
import com.douziit.meilianoa.base.utils.SpringUtil;
import com.douziit.meilianoa.base.utils.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.util.Base64Utils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * 头部参数校验拦截器
 *
 * @author MrShun
 * @version 1.0
 * @Date 2017-11-13
 */
public class HeaderRequiredInterceptor extends HandlerInterceptorAdapter {

	private static Logger LOG = LoggerFactory.getLogger(HeaderRequiredInterceptor.class);

	private Environment env;

	private UserTokenService userTokenService;



	/**
	 * 请求token开关, 请求签名开关, 签名过期时间
	 */
	private final String tokenCheckStr = "token.check", signatureCheckStr = "signature.check",
			signatureExpireTimeStr = "signature.expireTime";


	public HeaderRequiredInterceptor() {
		//appAuthService = SpringUtil.getBean(IAppThirdAuthService.class);
		env = SpringUtil.getBean(Environment.class);
		userTokenService = SpringUtil.getBean(UserTokenService.class);
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) {
		if ("OPTIONS".equals(request.getMethod())) {
			return false;
		}

		LOG.info("request--------------url：{}", request.getRequestURL());

		String requestURI = request.getRequestURI();
		LOG.info("request--------------uri：{}", request.getRequestURI());

		Method method = ((HandlerMethod) obj).getMethod();

		int status = response.getStatus();

		Boolean signatureCheck = env.getProperty(signatureCheckStr, Boolean.class, true);
		//判断是否需要检查签名
		NoCheckSignature noCheckSignature = AnnotationUtils.findAnnotation(method, NoCheckSignature.class);
		if (signatureCheck && noCheckSignature == null && status != 404) {
			checkSignature(request);
		}

		// 判断是否需要检查UserToken
		NoCheckToken noCheckToken = AnnotationUtils.findAnnotation(method, NoCheckToken.class);
		if (noCheckToken != null || status == 404) {
			return checkHeaders(method, request);
		} else {
			response.setHeader("content-type", "application/json;charset=UTF-8");
			// 检查是否存在Token
			// 配置是否校验，放过token方便前期调试
			Boolean tokenCheck = env.getProperty(tokenCheckStr, Boolean.class, true);
			if (tokenCheck) {
				String userTokenStr = request.getHeader(HeaderNameConstant.API_USER_TOKEN);
				if (StrUtil.isBlank(userTokenStr)) {
					response.setStatus(401);
					throw new TipException(401, "缺少" + HeaderNameConstant.API_USER_TOKEN + "参数或值为空!");
				}
				// 检查Token的有效性
				userTokenService.checkValidity(userTokenStr);
				// 检查权限,前期跳过
				// roleResourceService.selectAuthRoleResourceUrl(request);
			}
			return checkHeaders(method, request);
		}

	}

	/**
	 * 校验头部参数
	 *
	 * @param method
	 * @param request
	 * @return
	 */
	public boolean checkHeaders(Method method, HttpServletRequest request) {
		// 检查token
		HeaderRequired headerRequired = AnnotationUtils.findAnnotation(method, HeaderRequired.class);
		if (headerRequired == null) {
			return true;
		} else {
			// 获取头部注解里面的信息
			Class<? extends Annotation>[] headers = headerRequired.value();
			if (headers.length == 0) {
				return true;
			}
			for (Class<?> cls : headers) {
				if (cls == MobileLoc.class) {
					String locStr = request.getHeader(HeaderNameConstant.API_MOBILE_LOC);
					if (StrUtil.isBlank(locStr)) {
						throw new TipException("缺少" + HeaderNameConstant.API_MOBILE_LOC + "参数或值为空!");
					} else if (locStr.length() < 5 || locStr.indexOf(",") < 2
							|| locStr.indexOf(",") > locStr.length() - 2) {
						throw new TipException(HeaderNameConstant.API_MOBILE_LOC + "参数不合法, 当前值为 > " + locStr);
					}
				}
				// 处理DEVICE_NAME头部参数
				else if (cls == DeviceName.class) {
					String deviceNameStr = request.getHeader(HeaderNameConstant.API_DEVICE_NAME);
					if (StrUtil.isBlank(deviceNameStr)) {
						throw new TipException("缺少" + HeaderNameConstant.API_DEVICE_NAME + "参数或值为空!");
					}
				}
				// 处理User_Id头部参数
				else if (cls == UserId.class) {
					String userIdStr = request.getHeader(HeaderNameConstant.API_USER_ID);
					if (StrUtil.isBlank(userIdStr)) {
						throw new TipException("缺少" + HeaderNameConstant.API_USER_ID + "参数或值为空!");
					}
				}
				// 处理Push_Reg_Id头部参数
				else if (cls == UserId.class) {
					String pushRegIdStr = request.getHeader(HeaderNameConstant.API_PUSH_REG_ID);
					if (StrUtil.isBlank(pushRegIdStr)) {
						throw new TipException("缺少" + HeaderNameConstant.API_PUSH_REG_ID + "参数或值为空!");
					}
				}
			}
			return true;
		}
	}

	/**
	 * 为了系统安全, 做签名校验
	 *
	 * @param request
	 */
	private void checkSignature(HttpServletRequest request) {
		Map<String, String[]> parameterMap = request.getParameterMap();
		if (parameterMap != null && parameterMap.size() > 0) {
			TreeMap dataMap = new TreeMap(parameterMap);
			StringBuffer stringBuffer = new StringBuffer();
			Set<Map.Entry<String, String[]>> entries = dataMap.entrySet();
			for (Map.Entry<String, String[]> entry : entries) {
				String key = entry.getKey();
				String[] value = entry.getValue();
				if (value.length == 1) {
					stringBuffer.append(key + "=" + value[0] + "&");
				} else {
					stringBuffer.append(key + "=" + JacksonUtil.obj2json(value) + "&");
				}
			}
			String stringSignTemp = stringBuffer.substring(0, stringBuffer.length() - 1);
			// 拼接后的字符串
			LOG.debug("stringSignTemp:{}", stringSignTemp);
			String signature = Sign.sha256HMAC(stringSignTemp, SecurityConstant.SIGNATURE_KEY);
			String serverSignature = Base64Utils.encodeToString(signature.getBytes());
			// 服务端正确签名
			LOG.debug("Current ServerSignature:{}", serverSignature);
			String oauthSignature = request.getHeader(HeaderNameConstant.API_OAUTH_SIGNATURE);
			if (StrUtil.isBlank(oauthSignature)) {
				throw new TipException("缺少" + HeaderNameConstant.API_OAUTH_SIGNATURE + "参数或值为空!");
			} else if (!serverSignature.equals(oauthSignature)) {
				throw new TipException(ResponseCode.SIGNATURE_ERROR);
			} else {
				// 签名正确,再校验时间
				String requestTimeStamp = request.getParameter(SecurityConstant.SIGNATURE_FIELD);
				if (StrUtil.isBlank(requestTimeStamp)) {
					throw new TipException("客户端缺少" + SecurityConstant.SIGNATURE_FIELD + "请求参数!");
				} else {
					Long currentTimeStamp = System.currentTimeMillis();
					LOG.debug("requestTimeStamp:{}, currentTimeStamp:{}", requestTimeStamp, currentTimeStamp);
					Long signatureExpire = env.getProperty(signatureExpireTimeStr, Long.class, 7200000L);
					if (signatureExpire != 0 && currentTimeStamp - Long.valueOf(requestTimeStamp) > signatureExpire) {
						throw new TipException("客户端签名已过期, 重新请求!");
					}
				}
			}
		}
	}

	/**
	 * 为了系统安全, 做签名校验
	 *
	 * @param map
	 */
	public static void checkSignatures(HttpServletRequest request, LinkedHashMap map) {
		String oauthSignature = request.getHeader(HeaderNameConstant.API_OAUTH_SIGNATURE);
		String stringSignTemp = Sign.md5Sign(map, null);
		LOG.info("stringSignTemp:-----admin--------->" + stringSignTemp);
		String serverSignature = stringSignTemp.toUpperCase();
		// header传入的加密参数
		LOG.info("oauthSignature:-----app--------->" + oauthSignature);
		// 后台加密的参数
		LOG.info("serverSignature:-----admin--------->" + serverSignature);
		if (!serverSignature.equals(oauthSignature)) {
			throw new TipException(ResponseCode.SIGNATURE_ERROR);
		}
	}
}
